package com.chenyuxin.ilp.service.file.Impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.chenyuxin.ilp.exception.BaseException;
import com.chenyuxin.ilp.mapper.file.FileFolderMapper;
import com.chenyuxin.ilp.mapper.file.FileMetaMapper;
import com.chenyuxin.ilp.mapper.file.FilePermissionMapper;
import com.chenyuxin.ilp.mapper.file.FilePreviewMapper;
import com.chenyuxin.ilp.mapper.user.UserStorageMapper;
import com.chenyuxin.ilp.model.constant.FileExtensionConstant;
import com.chenyuxin.ilp.model.constant.FileSuccessConstant;
import com.chenyuxin.ilp.model.entity.file.FileFolder;
import com.chenyuxin.ilp.model.entity.file.FileMeta;
import com.chenyuxin.ilp.model.entity.file.FilePermission;
import com.chenyuxin.ilp.model.entity.file.FilePreview;
import com.chenyuxin.ilp.model.entity.user.UserStorage;
import com.chenyuxin.ilp.model.enums.exception.ExceptionEnums;
import com.chenyuxin.ilp.model.enums.exception.FileExceptionEnums;
import com.chenyuxin.ilp.model.enums.exception.UserExceptionEnums;
import com.chenyuxin.ilp.model.pojo.Result;
import com.chenyuxin.ilp.model.vo.file.FileConvertTask;
import com.chenyuxin.ilp.model.vo.file.FileVo;
import com.chenyuxin.ilp.model.vo.file.FolderParentVo;
import com.chenyuxin.ilp.model.vo.file.FolderVo;
import com.chenyuxin.ilp.service.file.FileService;
import com.chenyuxin.ilp.service.file.producer.ConvertFileProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class FileServiceImpl implements FileService {
    @Value("${minio.bucket.files}")
    private String bucket;
    @Autowired
    private MinioServiceImpl minioService;
    @Autowired
    private FileMetaMapper fileMetaMapper;
    @Autowired
    private UserStorageMapper userStorageMapper;
    @Autowired
    private FileFolderMapper fileFolderMapper;
    @Autowired
    private ConvertFileProducer convertFileProducer;
    @Autowired
    private FilePreviewMapper filePreviewMapper;
    @Autowired
    private FilePermissionMapper filePermissionMapper;

    @Transactional
    @Override
    public Result addFile(String userId, Long parentId, MultipartFile file) {
        //1 判断文件类型是否支持上传
        String fileName = file.getOriginalFilename();
        String extension = fileName.substring(fileName.lastIndexOf("."));
        if(!FileExtensionConstant.allowedUpload.contains(extension)){
            throw new BaseException(FileExceptionEnums.FILE_TYPE_CANT_UPLOAD);
        }
        //2 文件是否存在
        Boolean bool = this.existFile(userId,parentId, fileName);
        if(bool){
            throw new BaseException(FileExceptionEnums.FILE_EXIST);
        }
        //3 更新用户剩余存储空间
        double fileSize = file.getSize() / (1024.0 * 1024.0);
        UserStorage userStorage = userStorageMapper.selectById(userId);
        if(userStorage.getUsedSize()+fileSize>userStorage.getTotalSize()){
            throw new BaseException(UserExceptionEnums.STORAGE_FULL);
        }
        LambdaUpdateWrapper<UserStorage> storageWrapper = new LambdaUpdateWrapper<>();
        storageWrapper.eq(UserStorage::getId,userId)
                .set(UserStorage::getUsedSize,userStorage.getUsedSize()+fileSize)
                .set(UserStorage::getUpdateTime,LocalDateTime.now());
        userStorageMapper.update(storageWrapper);
        //4 存储用户文件信息到mysql
        FileMeta fileMeta = new FileMeta();
        fileMeta.setFileName(fileName);
        fileMeta.setParentId(parentId);
        fileMeta.setFileSize(file.getSize()/(1024.0*1024.0));
        fileMeta.setCreateTime(LocalDateTime.now());
        fileMeta.setUserId(userId);
        fileMeta.setIsOpen(0);
        fileMeta.setIsShare(0);
        fileMeta.setIsDelete(0);
        fileMetaMapper.insert(fileMeta);
        //5 存储用户文件到minio
         byte[] fileBytes = new byte[0];
         try {
             fileBytes= file.getBytes();
         } catch (Exception e){
             log.error("获取文件字节码文件失败");
             throw new BaseException(ExceptionEnums.SYSTEM_ERROR);
         }
        File tempFile = null;
        try{
            tempFile = File.createTempFile("minio", extension);
            //上传的文件拷贝到临时文件
            FileUtils.writeByteArrayToFile(tempFile,fileBytes);
        }catch (Exception exception){
            throw new BaseException(ExceptionEnums.SYSTEM_ERROR);
        }
        String absolutePath = tempFile.getAbsolutePath();
        String objectName = "/"+userId+"/file/"+fileMeta.getId()+extension;
        Boolean upload = minioService.upload(absolutePath, bucket, objectName,extension);
        if(!upload){
            throw new BaseException(FileExceptionEnums.FILE_UPLOAD_ERROR);
        }
        // 删除临时文件
        if (tempFile.exists()) {
            tempFile.delete();
        }
        //6 转换文件类型用于预览
        if(FileExtensionConstant.convertPreview.contains(extension)){
            FileConvertTask task = new FileConvertTask();
            try {
                task.setInitialFileId(fileMeta.getId());
                task.setExtension(extension);
                task.setUserId(userId);
                task.setFileBytes(fileBytes);
            } catch (Exception e) {
                log.error("封装文件类型转换消息,{}",e.getMessage());
                throw new BaseException(ExceptionEnums.SYSTEM_ERROR);
            }
            if(extension.equals(".docx")||extension.equals("doc")){
                convertFileProducer.sendWordToPDFTask(task);
            }else if (extension.equals(".xlsx")){
                convertFileProducer.sendExcelToPDFTask(task);
            }else {
                convertFileProducer.sendPPTToPDFTask(task);
            }
        }
        return Result.ok(FileSuccessConstant.UPLOAD_FILE_SUCCESS,null);
    }
    // 判断文件是否已经存在
    private Boolean existFile(String userId,Long parentId,String fileName){
        LambdaQueryWrapper<FileMeta> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileMeta::getIsDelete,0)
                .eq(FileMeta::getParentId,parentId)
                .eq(FileMeta::getFileName,fileName)
                .eq(FileMeta::getUserId,userId);
        FileMeta fileMeta = fileMetaMapper.selectOne(wrapper);
        if(fileMeta==null){
            return false;
        }
        return true;
    }
    // 查询文件树型结构
    @Override
    public Result selectFileTree(String userId) {
        LambdaQueryWrapper<FileFolder> folderWrapper = new LambdaQueryWrapper<>();
        folderWrapper.eq(FileFolder::getUserId,userId);
        List<FileFolder> fileFolders = fileFolderMapper.selectList(folderWrapper);
        LambdaQueryWrapper<FileMeta> fileWrapper = new LambdaQueryWrapper<>();
        fileWrapper.eq(FileMeta::getUserId,userId);
        fileWrapper.eq(FileMeta::getIsDelete,0);
        List<FileMeta> fileMetas = fileMetaMapper.selectList(fileWrapper);

        // 1. 先构建一个文件夹ID到FolderVo的映射
        Map<Long, FolderParentVo> folderMap = new HashMap<>();
        for (FileFolder folder : fileFolders) {
            FolderParentVo folderParentVo = BeanUtil.copyProperties(folder, FolderParentVo.class);
            folderParentVo.setChildren(new ArrayList<>());
            folderParentVo.setType(2);
            folderMap.put(folder.getId(), folderParentVo);
        }

        // 2. 遍历文件夹，找到对应的父节点并将文件夹添加到父节点的children中
        List<FolderParentVo> rootFolders = new ArrayList<>();
        for (FileFolder folder : fileFolders) {
            FolderParentVo folderParentVo = folderMap.get(folder.getId());
            if (folder.getParentId() == null) {
                rootFolders.add(folderParentVo); // 根节点直接加入
            } else {
                FolderParentVo parentFolderParentVo = folderMap.get(folder.getParentId());
                if (parentFolderParentVo != null) {
                    parentFolderParentVo.getChildren().add(folderParentVo); // 子节点加入到父节点的children中
                }
            }
        }

        // 3. 将文件加入到相应的文件夹中（文件夹不能作为父节点）
        for (FileMeta fileMeta : fileMetas) {
            FileVo fileVo = BeanUtil.copyProperties(fileMeta, FileVo.class);
            fileVo.setType(1);
            for (FolderParentVo folderParentVo : folderMap.values()) {
                if (fileVo.getParentId() != null && fileVo.getParentId().equals(folderParentVo.getId())) {
                    // 文件加入文件夹的children中
                    folderParentVo.getChildren().add(fileVo);
                }
            }
        }

        return Result.ok(rootFolders);
    }
    // 删除文件
    @Override
    public Result deleteFile(String userId, Long fileId) {
        //1 删除文件
        LambdaUpdateWrapper<FileMeta> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(FileMeta::getUserId,userId)
                .eq(FileMeta::getId,fileId)
                .eq(FileMeta::getIsDelete,0)
                .set(FileMeta::getIsDelete,1)
                .set(FileMeta::getDeleteTime,LocalDateTime.now());
        int update = fileMetaMapper.update(wrapper);
        if(update==0){
            throw new BaseException(FileExceptionEnums.FILE_NOT_EXIST);
        } else if (update>1) {
            throw new BaseException(ExceptionEnums.SYSTEM_ERROR);
        }
        //2 删除文件共享权限
        LambdaQueryWrapper<FilePermission> filePermissionWrapper = new LambdaQueryWrapper<>();
        filePermissionWrapper.eq(FilePermission::getFileId,fileId)
                .eq(FilePermission::getHostUserId,userId);
        filePermissionMapper.delete(filePermissionWrapper);
        return Result.ok(FileSuccessConstant.DELETE_FILE_SUCCESS,null);
    }
    // 批量删除文件
    @Transactional
    @Override
    public Result deleteFileBatch(String userId, List<Long> fileIds) {
        // 批量删除文件
        LambdaUpdateWrapper<FileMeta> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(FileMeta::getUserId, userId)
                .in(FileMeta::getId, fileIds)
                .set(FileMeta::getIsDelete, 1)
                .set(FileMeta::getDeleteTime,LocalDateTime.now());
        fileMetaMapper.update(wrapper);
        // 批量删除文件权限
        LambdaQueryWrapper<FilePermission> filePermissionWrapper = new LambdaQueryWrapper<>();
        filePermissionWrapper.in(FilePermission::getFileId,fileIds)
                .eq(FilePermission::getHostUserId,userId);
        filePermissionMapper.delete(filePermissionWrapper);
        return Result.ok(FileSuccessConstant.DELETE_FILE_SUCCESS,null);
    }
    // 根据父节点id查询子节点
    @Override
    public Result selectFileByParentId(String userId, Long folderId) {
        LambdaQueryWrapper<FileFolder> folderWrapper = new LambdaQueryWrapper<>();
        folderWrapper.eq(FileFolder::getUserId,userId)
                .eq(FileFolder::getParentId,folderId);
        List<FileFolder> fileFolders = fileFolderMapper.selectList(folderWrapper);
        LambdaQueryWrapper<FileMeta> fileWrapper = new LambdaQueryWrapper<>();
        fileWrapper.eq(FileMeta::getUserId,userId)
                .eq(FileMeta::getParentId,folderId)
                .eq(FileMeta::getIsDelete,0);
        List<FileMeta> fileMetas = fileMetaMapper.selectList(fileWrapper);
        List<Object> list = new ArrayList<>();
        for (FileFolder folder:fileFolders){
            FolderVo folderVo = BeanUtil.copyProperties(folder, FolderVo.class);
            folderVo.setType(2);
            list.add(folderVo);
        }
        for(FileMeta fileMeta:fileMetas){
            FileVo fileVo = BeanUtil.copyProperties(fileMeta, FileVo.class);
            fileVo.setType(1);
            list.add(fileVo);
        }
        return Result.ok(list);
    }
    // 文件预览
    @Override
    public Result previewFile(Long fileId, String userId) {
        //1 查询文件信息
        LambdaQueryWrapper<FileMeta> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileMeta::getUserId,userId)
                .eq(FileMeta::getId,fileId)
                .eq(FileMeta::getIsDelete,0);
        FileMeta fileMeta = fileMetaMapper.selectOne(wrapper);
        if(fileMeta==null){
            throw new BaseException(FileExceptionEnums.FILE_NOT_EXIST);
        }
        //2 生成预览url
        String fileName = fileMeta.getFileName();
        String extension = fileName.substring(fileName.lastIndexOf("."));
        String fileObjectName;
        if (FileExtensionConstant.allowedPreview.contains(extension)) {
            fileObjectName = "/" + userId +"/file/"+fileId+extension;
        } else if (FileExtensionConstant.convertPreview.contains(extension)) {
            LambdaQueryWrapper<FilePreview> previewWrapper = new LambdaQueryWrapper<>();
            previewWrapper.eq(FilePreview::getInitialFileId,fileId);
            FilePreview filePreview = filePreviewMapper.selectOne(previewWrapper);
            fileObjectName = "/"+userId + "/preview" + "/" + filePreview.getPreviewFileName();
        }else {
            throw new BaseException(FileExceptionEnums.FILE_CANT_PREVIEW);
        }
        String previewUrl = minioService.getPreviewUrl(bucket, fileObjectName);
        return Result.ok(FileSuccessConstant.GET_PREVIEW_URL_SUCCESS,previewUrl);
    }
    // 修改文件公开权限
    @Override
    public Result updateFileOpen(Long fileId, Integer permission, String userId) {
        LambdaUpdateWrapper<FileMeta> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(FileMeta::getId,fileId)
                .eq(FileMeta::getUserId,userId)
                .eq(FileMeta::getIsDelete,0)
                .set(FileMeta::getIsOpen,permission);
        fileMetaMapper.update(wrapper);
        return Result.ok(FileSuccessConstant.UPDATE_FILE_PERMISSION_SUCCESS,null);
    }
    // 下载文件
    @Override
    public ResponseEntity<byte[]> downloadFile(String userId, Long fileId) {
        LambdaQueryWrapper<FileMeta> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileMeta::getId,fileId)
                .eq(FileMeta::getUserId,userId)
                .eq(FileMeta::getIsDelete,0);
        FileMeta fileMeta = fileMetaMapper.selectOne(wrapper);
        if(fileMeta==null){
            log.error("下载文件不存在,fileId:{}",fileId);
            throw new BaseException(FileExceptionEnums.FILE_NOT_EXIST);
        }
        String fileName = fileMeta.getFileName();
        String extension = fileName.substring(fileName.lastIndexOf("."));
        String objectName = "/"+userId+"/file/"+fileId+extension;
        byte[] fileByte = minioService.download(objectName);
        HttpHeaders headers = new HttpHeaders();
        try {
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileMeta.getFileName(), StandardCharsets.UTF_8.toString()));
        } catch (UnsupportedEncodingException e) {
            log.error("封装ResponseEntity出现异常,objectName:{},errorMessage:{}",objectName,e.getMessage());
            throw new BaseException(ExceptionEnums.SYSTEM_ERROR);
        }
        return new ResponseEntity<>(fileByte,headers, HttpStatus.OK);
    }
}
